perm filename DVX.FIX[MF,ALS] blob sn#788399 filedate 1985-03-29 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00007 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	@p function in_TFM(@!z:integer):boolean {input \.{TFM} data or return |false|}
C00007 00003
C00017 00004	@x Opening packed binary files:
C00037 00005	@p function in_TFM(@!z:integer):boolean {input \.{TFM} data or return |false|}
C00047 00006	module 128
C00058 00007
C00059 ENDMK
C⊗;
@p function in_TFM(@!z:integer):boolean; {input \.{TFM} data or return |false|}
label 9997, {go here when the format is bad}
	9998,	{go here when the information cannot be loaded}
	9999;	{go here to exit}
var k:integer; {index for loops}
@!lh:integer; {length of the header data, in four-byte words}
@!nw:integer; {number of words in the width table}
@!wp:0..max_glyphs; {new value of |width_ptr| after successful input}
@!alpha,@!beta:integer; {quantities used in the scaling computation}
begin @<Read past the header data; |goto 9997| if there is a problem@>;
@<Store character-width indices at the end of the |width| table@>;
@<Read and convert the width values, setting up the |in_width| table@>;
@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>;
width_ptr←wp; in_TFM←true; goto 9999;
9997: print_ln('---not loaded, TFM file is bad');
@.TFM file is bad@>
9998: in_TFM←false;
9999: end;

@ @<Read past the header...@>=
read_tfm_word; lh←b2*256+b3;
read_tfm_word; font_bc[nf]←b0*256+b1; font_ec[nf]←b2*256+b3;
if font_ec[nf]<font_bc[nf] then font_bc[nf]←font_ec[nf]+1;
if width_ptr+font_ec[nf]-font_bc[nf]+1>max_widths then
	begin print_ln('---not loaded, DVItype needs larger width table');
@.DVItype needs larger...@>
		goto 9998;
	end;
wp←width_ptr+font_ec[nf]-font_bc[nf]+1;
read_tfm_word; nw←b0*256+b1;
if (nw=0)∨(nw>256) then goto 9997;
for k←1 to 3+lh do
	begin if eof(tfm_file) then goto 9997;
	read_tfm_word;
	if k=4 then
		if b0<128 then tfm_check_sum←((b0*256+b1)*256+b2)*256+b3
		else tfm_check_sum←(((b0-256)*256+b1)*256+b2)*256+b3;
	end;

@ @<Store character-width indices...@>=
if wp>0 then for k←width_ptr to wp-1 do
	begin read_tfm_word;
	if b0>nw then goto 9997;
	width[k]←b0;
	end;

@ @<Read and convert the width values...@>=
@<Replace |z| by $|z|↑\prime$ and compute $\alpha,\beta$@>;
for k←0 to nw-1 do
	begin read_tfm_word;
	in_width[k]←(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
	if b0>0 then if b0<255 then goto 9997
		else in_width[k]←in_width[k]-alpha;
	end

@ @<Move the widths from |in_width| to |width|, and append |pixel_width| values@>=
if in_width[0]≠0 then goto 9997; {the first width should be zero}
data_base[nf]←width_ptr-font_bc[nf];
if wp>0 then for k←width_ptr to wp-1 do
	if width[k]=0 then
		begin width[k]←invalid_width; pixel_width[k]←0;
		end
	else	begin width[k]←in_width[width[k]];
		pixel_width[k]←pixel_round(width[k]);
		end





@x The default font directory:
@d default_directory_name=='TeXfonts:' {change this to the correct name}
@d default_directory_name_length=9 {change this to the correct length}
@y
@d default_directory_name=='[TEX,SYS]' {change this to the correct name}
@d default_directory_name_length=9 {change this to the correct length}
@z


@x Change to file name conventions:
@ The string |cur_name| is supposed to be set to the external name of the
\.{TFM} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.
@↑system dependencies@>

@<Move font name into the |cur_name| string@>=
for k←1 to name_length do cur_name[k]←' ';
if p=0 then
	begin for k←1 to default_directory_name_length do
		cur_name[k]←default_directory[k];
	r←default_directory_name_length;
	end
else r←0;
for k←font_name[nf] to font_name[nf+1]-1 do
	begin incr(r);
	if r+4>name_length then
		abort('DVItype capacity exceeded (max font name length=',
			name_length:1,')!');
@.DVItype capacity exceeded...@>
	if (names[k]≥"a")∧(names[k]≤"z") then
			cur_name[r]←xchr[names[k]-@'40]
	else cur_name[r]←xchr[names[k]];
	end;
cur_name[r+1]←'.'; cur_name[r+2]←'T'; cur_name[r+3]←'F'; cur_name[r+4]←'M'
@y
@ The string |cur_name| is supposed to be set to the external name of the
\.{TFM} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the suffix `\.{.TFM}'. But at {\mc SAIL} we append the
directory name after the font name. And we compress `\.{oldenglish}' to
`\.{oldish}'.
@↑system dependencies@>

@<Move font name into the |cur_name| string@>=
for k←1 to name_length do cur_name[k]←' ';
r←0;
for k←font_name[nf]+p to font_name[nf+1]-1 do
    if (k≤font_name[nf]+p+2)∨(k≥font_name[nf+1]-3) then
	begin incr(r);
	if r+4>name_length then
		abort('DVItype capacity exceeded (max font name length=',
			name_length:1,')!');
@.DVItype capacity exceeded...@>
	if (names[k]≥"a")∧(names[k]≤"z") then
			cur_name[r]←xchr[names[k]-@'40]
	else cur_name[r]←xchr[names[k]];
	end;
cur_name[r+1]←'.'; cur_name[r+2]←'T'; cur_name[r+3]←'F'; cur_name[r+4]←'M';
r←r+4;
if p=0 then for k←1 to default_directory_name_length do
	begin incr(r);
	if r>name_length then abort('Font name is too long!');
	cur_name[r]←default_directory[k];
	end
else for k←font_name[nf] to font_name[nf]+p-1 do
	begin incr(r);
	if r>name_length then abort('Font name is too long!');
	if (names[k]≥"a")∧(names[k]≤"z") then
			cur_name[r]←xchr[names[k]-@'40]
	else cur_name[r]←xchr[names[k]];
	end
@z
@x Opening packed binary files:
@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file);
cur_loc←0;
end;
@#
procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
begin reset(gf_file,cur_name);
cur_gf_loc←0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
begin reset(tfm_file,cur_tfm_name);
end;
@y
@p procedure esp(var dvi_file,im_file:f@&i@&l@&e); extern; @t\2@>@/
	{spools the |im_file| under the |dvi_file| name}
@#
function rescan:boolean; extern; @t\2@>@;
	{puts the command line into the terminal buffer,
	or returns |false| if there was no command line}
@#
procedure cur_nam(var chan:f@&i@&l@&e;var s:string); extern; @t\2@>@/
@#
function erstat(var f:file):integer; extern;@t/2@>
@#
procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
label 223;
var i:integer;
@!seen_dot, @!seen_left_bracket, @!seen_right_bracket: boolean;
begin
if rescan then begin
	read_ln(tty);
	while (¬ eoln(tty))∧(tty↑≠';') do get(tty);
	end;
if eoln(tty) then write(tty,'DVI file? ');
223:
i←1;
get(tty);
seen_dot←false; seen_left_bracket←false; seen_right_bracket←false;
while ¬ eoln(tty) do begin
	if tty↑='.' then seen_dot←true
	else if tty↑=']' then seen_right_bracket←true
	else if tty↑='[' then begin
		if not seen_dot then begin
			seen_dot←true;
			dvi_name[i]←'.'; incr(i);
			dvi_name[i]←'D'; incr(i);
			dvi_name[i]←'V'; incr(i);
			dvi_name[i]←'I'; incr(i);
			end;
		seen_left_bracket←true
		end;
	dvi_name[i]←tty↑; incr(i); get(tty);
	end;
if not seen_dot then begin
	dvi_name[i]←'.'; incr(i);
	dvi_name[i]←'D'; incr(i);
	dvi_name[i]←'V'; incr(i);
	dvi_name[i]←'I'; incr(i);
	end;
if seen_left_bracket and not seen_right_bracket then begin
	dvi_name[i]←']'; incr(i);
	end;
while i<f_name_size do begin dvi_name[i]←' '; incr(i); end;
reset(dvi_file,dvi_name,'/B:8/N:9/O');
if (erstat(dvi_file) mod @'20000) > 0 then begin
	write(tty,'.DVI file? ');
	goto 223;
	end;
cur_loc←0;
end;
@#
procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
begin reset(gf_file,cur_name,'/B:8/O/N:9');
cur_gf_loc←0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
begin
reset(tfm_file,cur_tfm_name,'/B:8/O/N:9');
end;
@z
-------------------

@ We will refer to \.{TFM} files for character width information in those
cases where \.{.GF} files are not available.  We read four bytes at a
time, putting the input into global
variables |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and
|b3| the fourth.

@<Glob...@>=
@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}

@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
four bytes in the current \.{TFM} file.
@↑system dependencies@>

@p procedure read_tfm_word;
begin read(tfm_file,b0); read(tfm_file,b1);
read(tfm_file,b2); read(tfm_file,b3);
end;

---------------
@p procedure get_tfm_file;
var 
@!p:integer; {length of the area/directory spec}
@!n:integer; {length of the font name proper}
@!c,@!q,@!d:integer; {check sum, scaled size, and design size}
@!r:0..name_length; {index into |cur_name|}
@!j,@!k:0..name_size; {indices into |names|}
@!m: integer; {available for use in |mag| effect caculations}
@!mismatch:boolean; {do names disagree?}
begin
m←font_m_val[cur_font];
p←font_a_val[cur_font];
n←font_l_val[cur_font];
@<Move font name into the |cur_tfn_name| string@>;
@!debug
print_font(cur_font); print('.',m:1);
print('(',cur_font:1,') ');
gubed@/
open_tfm_file;
if eof(tfm_file) then
    begin
    print_ln('');
    print_font(cur_font); print('.',m:1);
    print_ln(' is not available either in gf or tfm form.');
    @.TFM file can\'t be opened@>
    font_state[cur_font]←1;
    end
else
    begin
    print_ln('');
    print_font(cur_font); print('.',m:1);
    print_ln(' can not be found, will leave |tfm|-determined spaces.');
    end;
end;

@ The string |cur_tfm_name| is supposed to be set to the external name of the
\.{TFM} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.
@↑system dependencies@>

@<Glob...@>=
@!cur_tfm_name:packed array[1..name_length] of char; {external name,
	with no lower case letters}

@<Move font name into the |cur_tfm_name| string@>=
for k←1 to name_length do cur_tfm_name[k]←' ';
if p=0 then
	begin for k←1 to default_directory_name_length do
		cur_tfm_name[k]←default_directory[k];
	r←default_directory_name_length;
	end
else r←0;
for k←font_name[cur_font] to font_name[cur_font+1]-1 do
	begin incr(r);
	if r+5>name_length then
		abort('DVIIMP capacity exceeded (max font name length=',
			name_length:1,')!');
@.DVIIMP capacity exceeded...@>
	if (names[k]≥"a")∧(names[k]≤"z") then
			cur_name[r]←xchr[names[k]-@'40]
	else cur_name[r]←xchr[names[k]];
	end;
cur_tfm_name[r+1]←'.';
cur_tfm_name[r+2]←'T'; cur_tfm_name[r+3]←'F'; cur_tfm_name[r+4]←'M';

---------------
working material 
@x
@p procedure open_im_file; {prepares to write packed bytes in |im_file|}
begin rewrite(im_file); im_byte_no←0;
end;
@y
@p procedure open_im_file; {prepares to write packed bytes in |im_file|}
label done;
var i,j:integer;
begin
cur_nam(dvi_file,imp_name);
i←1;
while imp_name[i]>'@@' do i←i+1; {skip to dot or bracket or space}
j←i-1;
imp_name[i]←'.'; incr(i);
imp_name[i]←'L'; incr(i);
imp_name[i]←'P'; incr(i);
imp_name[i]←'T'; incr(i);
imp_name[i]←'['; incr(i);
imp_name[i]←'S'; incr(i);
imp_name[i]←'P'; incr(i);
imp_name[i]←'L'; incr(i);
imp_name[i]←','; incr(i);
imp_name[i]←'S'; incr(i);
imp_name[i]←'Y'; incr(i);
imp_name[i]←'S'; incr(i);
imp_name[i]←']'; incr(i);
imp_name[i]←' ';
while true do begin
	reset(im_file,imp_name,'/O');
	if (erstat(im_file) mod @'20000)>0 then goto done;
	imp_name[j]←chr(ord(imp_name[j])+1);
	if imp_name[j]>'Z' then begin
		imp_name[j-1]←chr(ord(imp_name[j-1])+1);
		imp_name[j]←'A';
		if imp_name[j-1]>'Z' then imp_name[j-1]←'A';
		end;
	end;
done:
rewrite(im_file,imp_name,'/B:8/N:9/P:256');
im_byte_no←0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
begin reset(tfm_file,cur_name);
end;
@z


@x Opening packed binary files:
@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file);
cur_loc←0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
begin reset(tfm_file,cur_name);
end;
@y
@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file,'','/B:8');
cur_loc←0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
begin reset(tfm_file,cur_name,'/B:8/O/N:9');
end;
@z

@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file);
cur_loc←0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
begin reset(tfm_file,cur_name);
end;


@ The following subroutine does the necessary things when a \\{fnt\_def}
command is being processed.

@p procedure define_font(@!e:integer); {|e| is an external font number}
var f:0..max_fonts;
@!p:integer; {length of the area/directory spec}
@!n:integer; {length of the font name proper}
@!c,@!q,@!d:integer; {check sum, scaled size, and design size}
@!r:0..name_length; {index into |cur_name|}
@!j,@!k:0..name_size; {indices into |names|}
@!mismatch:boolean; {do names disagree?}
begin if nf=max_fonts then abort('DVItype capacity exceeded (max fonts=',
		max_fonts:1,')!');
@.DVItype capacity exceeded...@>
font_num[nf]←e; f←0;
while font_num[f]≠e do incr(f);
@<Read the font parameters into position for font |nf|, and
	print the font name@>;
if ((out_mode=the_works)∧ in_postamble)∨@|
	 ((out_mode<the_works)∧ ¬ in_postamble) then
	begin if f<nf then print_ln('---this font was already defined!');
@.this font was already defined@>
	end
else	begin if f=nf then print_ln('---this font wasn''t loaded before!');
@.this font wasn't loaded before@>
	end;
if f=nf then @<Load the new font, unless there are problems@>
else @<Check that the current font definition matches the old one@>;
end;

@ @<Check that the current...@>=
begin if font_check_sum[f]≠c then
	print_ln('---check sum doesn''t match previous definition!');
@.check sum doesn't match@>
if font_scaled_size[f]≠q then
	print_ln('---scaled size doesn''t match previous definition!');
@.scaled size doesn't match@>
if font_design_size[f]≠d then
	print_ln('---design size doesn''t match previous definition!');
@.design size doesn't match@>
j←font_name[f]; k←font_name[nf]; mismatch←false;
while j<font_name[f+1] do
	begin if names[j]≠names[k] then mismatch←true;
	incr(j); incr(k);
	end;
if k≠font_name[nf+1] then mismatch←true;
if mismatch then print_ln('---font name doesn''t match previous definition!');
@.font name doesn't match@>
end

@ @<Read the font parameters into position for font |nf|...@>=
c←signed_quad; font_check_sum[nf]←c;@/
q←signed_quad; font_scaled_size[nf]←q;@/
d←signed_quad; font_design_size[nf]←d;@/
p←get_byte; n←get_byte;
if font_name[nf]+n+p>name_size then
	abort('DVItype capacity exceeded (name size=',name_size:1,')!');
@.DVItype capacity exceeded...@>
font_name[nf+1]←font_name[nf]+n+p;
if showing then print(': ')
	{when |showing| is true, the font number has already been printed}
else print('Font ',e:1,': ');
if n+p=0 then print('null font name!')
@.null font name@>
else for k←font_name[nf] to font_name[nf+1]-1 do names[k]←get_byte;
incr(nf); print_font(nf-1); decr(nf)

@ @<Load the new font, unless there are problems@>=
begin @<Move font name into the |cur_name| string@>;
open_tfm_file;
if eof(tfm_file) then
	print('---not loaded, TFM file can''t be opened!')
@.TFM file can\'t be opened@>
else	begin if (q≤0)∨(q≥@'1000000000) then
		print('---not loaded, bad scale (',q:1,')!')
@.bad scale@>
	else if (d≤0)∨(d≥@'1000000000) then
		print('---not loaded, bad design size (',d:1,')!')
@.bad design size@>
	else if in_TFM(q) then @<Finish loading the new font info@>;
	end;
if out_mode=errors_only then print_ln(' ');
end

@ @<Finish loading...@>=
begin font_space[nf]←q div 6; {this is a 3-unit ``thin space''}
if (c≠0)∧(tfm_check_sum≠0)∧(c≠tfm_check_sum) then
	begin print_ln('---beware: check sums do not agree!');
@.beware: check sums do not agree@>
@.check sums do not agree@>
	print_ln('   (',c:1,' vs. ',tfm_check_sum:1,')');
	print('   ');
	end;
print('---loaded at size ',q:1,' DVI units');
d←round((100.0*conv*q)/(true_conv*d));
if d≠100 then
	begin print_ln(' '); print(' (this font is magnified ',d:1,'%)');
	end;
@.this font is magnified@>
incr(nf); {now the new font is officially present}
font_space[nf]←0; {for |out_space| and |out_vmove|}
end

@ If |p=0|, i.e., if no font directory has been specified, \.{DVItype}
is supposed to use the default font directory, which is a
system-dependent place where the standard fonts are kept.
The string variable |default_directory| contains the name of this area.
@↑system dependencies@>

@d default_directory_name=='TeXfonts:' {change this to the correct name}
@d default_directory_name_length=9 {change this to the correct length}

@<Glob...@>=
@!default_directory:packed array[1..default_directory_name_length] of char;

@ @<Set init...@>=
default_directory←default_directory_name;

@ The string |cur_name| is supposed to be set to the external name of the
\.{TFM} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.
@↑system dependencies@>

@<Move font name into the |cur_name| string@>=
for k←1 to name_length do cur_name[k]←' ';
if p=0 then
	begin for k←1 to default_directory_name_length do
		cur_name[k]←default_directory[k];
	r←default_directory_name_length;
	end
else r←0;
for k←font_name[nf] to font_name[nf+1]-1 do
	begin incr(r);
	if r+4>name_length then
		abort('DVItype capacity exceeded (max font name length=',
			name_length:1,')!');
@.DVItype capacity exceeded...@>
	if (names[k]≥"a")∧(names[k]≤"z") then
			cur_name[r]←xchr[names[k]-@'40]
	else cur_name[r]←xchr[names[k]];
	end;
cur_name[r+1]←'.'; cur_name[r+2]←'T'; cur_name[r+3]←'F'; cur_name[r+4]←'M'
@p function in_TFM(@!z:integer):boolean; {input \.{TFM} data or return |false|}
label 9997, {go here when the format is bad}
	9998,	{go here when the information cannot be loaded}
	9999;	{go here to exit}
var k:integer; {index for loops}
@!lh:integer; {length of the header data, in four-byte words}
@!nw:integer; {number of words in the width table}
@!wp:0..max_widths; {new value of |width_ptr| after successful input}
@!alpha,@!beta:integer; {quantities used in the scaling computation}
begin @<Read past the header data; |goto 9997| if there is a problem@>;
@<Store character-width indices at the end of the |width| table@>;
@<Read and convert the width values, setting up the |in_width| table@>;
@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>;
width_ptr←wp; in_TFM←true; goto 9999;
9997: print_ln('---not loaded, TFM file is bad');
@.TFM file is bad@>
9998: in_TFM←false;
9999: end;

@ @<Read past the header...@>=
read_tfm_word; lh←b2*256+b3;
read_tfm_word; font_bc[nf]←b0*256+b1; font_ec[nf]←b2*256+b3;
if font_ec[nf]<font_bc[nf] then font_bc[nf]←font_ec[nf]+1;
if width_ptr+font_ec[nf]-font_bc[nf]+1>max_widths then
	begin print_ln('---not loaded, DVItype needs larger width table');
@.DVItype needs larger...@>
		goto 9998;
	end;
wp←width_ptr+font_ec[nf]-font_bc[nf]+1;
read_tfm_word; nw←b0*256+b1;
if (nw=0)∨(nw>256) then goto 9997;
for k←1 to 3+lh do
	begin if eof(tfm_file) then goto 9997;
	read_tfm_word;
	if k=4 then
		if b0<128 then tfm_check_sum←((b0*256+b1)*256+b2)*256+b3
		else tfm_check_sum←(((b0-256)*256+b1)*256+b2)*256+b3;
	end;

@ @<Store character-width indices...@>=
if wp>0 then for k←width_ptr to wp-1 do
	begin read_tfm_word;
	if b0>nw then goto 9997;
	width[k]←b0;
	end;

@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>=
if in_width[0]≠0 then goto 9997; {the first width should be zero}
width_base[nf]←width_ptr-font_bc[nf];
if wp>0 then for k←width_ptr to wp-1 do
	if width[k]=0 then
		begin width[k]←invalid_width; pixel_width[k]←0;
		end
	else	begin width[k]←in_width[width[k]];
		pixel_width[k]←pixel_round(width[k]);
		end
module 128
if g≠-1 then
    begin
PUT THE if p>128 stuff here
    end;



@ To avoid confusion the |tfm| derived width data will be stored in
temporary arrays indicated by prefacing the letterd |tfm_| and only after
the computations are done will we store the resulting data in the normal
arrays.

@p function in_tfm(@!z:integer):boolean; {input \.{TFM} data or return |false|}
label 9997, {go here when the format is bad}
	9998,	{go here when the information cannot be loaded}
	9999;	{go here to exit}
var k:integer; {index for loops}
@!lh:integer; {length of the header data, in four-byte words}
@!nw:integer; {number of words in the tfm_width table}
@!wp:0..max_widths; {new value of |width_ptr| after successful input}
@!alpha,@!beta:integer; {quantities used in the scaling computation}
@!tfm_width:array[0..max_widths] of integer; {actual character widths,
@!tfm_pixel_width:array[0..max_widths] of integer; {actual character widths,
begin
@<Read past the header data; |goto 9997| if there is a problem@>;
@<Store character-tfm_width indices at the end of the |tfm_width| table@>;
@<Read and convert the tfm_width values, setting up the |in_width| table@>;
@<Move the widths from |in_width| to |tfm_width|, and append |tfm_pixel_width| values@>;
width_ptr←wp; in_TFM←true; goto 9999;
9997: print_ln('---not loaded, TFM file is bad');
@.TFM file is bad@>
9998: in_TFM←false;
9999: end;

@ @<Read past the header...@>=
read_tfm_word; lh←b2*256+b3;
read_tfm_word; font_bc[nf]←b0*256+b1; font_ec[nf]←b2*256+b3;
if font_ec[nf]<font_bc[nf] then font_bc[nf]←font_ec[nf]+1;
if width_ptr+font_ec[nf]-font_bc[nf]+1>max_widths then
	begin print_ln('---not loaded, DVItype needs larger tfm_width table');
@.DVItype needs larger...@>
		goto 9998;
	end;
wp←width_ptr+font_ec[nf]-font_bc[nf]+1;
read_tfm_word; nw←b0*256+b1;
if (nw=0)∨(nw>256) then goto 9997;
for k←1 to 3+lh do
	begin if eof(tfm_file) then goto 9997;
	read_tfm_word;
	if k=4 then
		if b0<128 then tfm_check_sum←((b0*256+b1)*256+b2)*256+b3
		else tfm_check_sum←(((b0-256)*256+b1)*256+b2)*256+b3;
	end;

@ @<Store character-tfm_width indices...@>=
if wp>0 then for k←width_ptr to wp-1 do
	begin read_tfm_word;
	if b0>nw then goto 9997;
	tfm_width[k]←b0;
	end;

@ The most important part of |in_TFM| is the tfm_width computation, which
involves multiplying the relative widths in the \.{TFM} file by the
scaling factor in the \.{DVI} file. This fixed-point multiplication
must be done with precisely the same accuracy by all \.{DVI}-reading programs,
in order to validate the assumptions made by \.{DVI}-writing programs
like \TeX82.

Let us therefore summarize what needs to be done. Each tfm_width in a \.{TFM}
file appears as a four-byte quantity called a |fix_word|.  A |fix_word|
whose respective bytes are $(a,b,c,d)$ represents the number
$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
b\cdot2↑{-4}+c\cdot2↑{-12}+d\cdot2↑{-20}&a=0;\cr
-16+b\cdot2↑{-4}+c\cdot2↑{-12}+d\cdot2↑{-20}&a=255.\cr}}\right.$$
(No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
dimension must be less than 16.)  We want to multiply this quantity by the
integer~|z|, which is known to be less than $2↑{27}$. Let $\alpha=16z$.
If $|z|<2↑{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
16, to obtain a multiplier less than $2↑{23}$, and we can compensate for
this later. If |z| has thereby been replaced by $|z|↑\prime=|z|/2↑e$, let
$\beta=2↑{4-e}$; we shall compute
$$\lfloor(b+c\cdot2↑{-8}+d\cdot2↑{-16})\,z↑\prime/\beta\rfloor$$ if $a=0$,
or the same quantity minus $\alpha$ if $a=255$.  This calculation must be
done exactly, for the reasons stated above; the following program does the
job in a system-independent way, assuming that arithmetic is exact on
numbers less than $2↑{31}$ in magnitude.

	@<Read and convert the tfm_width values...@>=
	@<Replace |z| by $|z|↑\prime$ and compute $\alpha,\beta$@>;
	for k←0 to nw-1 do
		begin read_tfm_word;
		in_width[k]←(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
		if b0>0 then if b0<255 then goto 9997
			else in_width[k]←in_width[k]-alpha;
		end

	@ @<Replace |z|...@>=
	begin alpha←16*z; beta←16;
	while z≥@'40000000 do
		begin z←z div 2; beta←beta div 2;
		end;
	end

@ A \.{DVI}-reading program usually works with font files instead of
\.{TFM} files, so \.{DVItype} is atypical in that respect. Font files
should, however, contain exactly the same character tfm_width data that is
found in the corresponding \.{TFM}s; check sums are used to help
ensure this. In addition, font files usually also contain the widths of
characters in pixels, since the device-independent character widths of
\.{TFM} files are generally not perfect multiples of pixels.

The |tfm_pixel_width| array contains this information; when |tfm_width[k]| is the
device-independent tfm_width of some character in \.{DVI} units, |tfm_pixel_width[k]|
is the corresponding tfm_width of that character in an actual font.
The macro |char_pixel_width| is set up to be analogous to |char_width|.

@d char_pixel_width(#)==tfm_pixel_width[data_base[#]+char_width_end

@<Glob...@>=
	in pixels}
@!conv:real; {converts \.{DVI} units to pixels}
@!true_conv:real; {converts unmagnified \.{DVI} units to pixels}
@!numerator,@!denominator:integer; {stated conversion ratio}
@!mag:integer; {magnification factor times 1000}

@ The following code computes pixel widths by simply rounding the \.{TFM}
widths to the nearest integer number of pixels, based on the conversion factor
|conv| that converts \.{DVI} units to pixels. However, such a simple
formula will not be valid for all fonts, and it will often give results that
are off by $\pm1$ when a low-resolution font has been carefully
hand-fitted. For example, a font designer often wants to make the letter `m'
a pixel wider or narrower in order to make the font appear more consistent.
\.{DVI}-to-printer programs should therefore input the correct pixel tfm_width
information from font files whenever there is a chance that it may differ.
A warning message may also be desirable in the case that at least one character
is found whose pixel tfm_width differs from |conv*tfm_width| by more than a full pixel.
@↑system dependencies@>

@d pixel_round(#)==round(conv*(#))

@<Move the widths from |in_width| to |tfm_width|, and append |tfm_pixel_width| values@>=
if in_width[0]≠0 then goto 9997; {the first tfm_width should be zero}
data_base[nf]←width_ptr-font_bc[nf];
if wp>0 then for k←width_ptr to wp-1 do
	if tfm_width[k]=0 then
		begin tfm_width[k]←invalid_width; tfm_pixel_width[k]←0;
		end
	else	begin tfm_width[k]←in_width[tfm_width[k]];
		tfm_pixel_width[k]←pixel_round(tfm_width[k]);
		end